﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel.Composition;
using ShredTheLog.Api;
using ShredTheLog.Api.DataContracts.TextChat;
using System.Media;
using System.Windows.Threading;
using System.Diagnostics;
using ShredTheLog.Api.DataContracts;
using System.Windows.Media;
using System.Windows;
using ShredTheLog.Views;
using ShredTheLog.ViewModels;
using System.Disposables;
using System.Diagnostics.Contracts;
using ShredTheLog.Converters;

namespace ShredTheLog.Plugins
{
    public static class NotificationHelper
    {
        public static Window GetTextChatWindow(string roomName)
        {
            return Application.Current.Windows.OfType<TextChatRoomView>().Single(i => ((TextChatRoomViewModel)i.DataContext).RoomName == roomName);
        }

        public static bool IsMessageNotificationNeeded(IAddMessageUpdate notification, INotifierSettings settings)
        {
            var textChatWindow = GetTextChatWindow(notification.RoomName);
            var isPublicChatRoom = ((TextChatRoomViewModel)textChatWindow.DataContext).RoomName.StartsWith(RoomConverter.PublicRoomPrefix);
            return settings.IsActive &&
                (settings.NotifyOnMessagesInActiveChatWindows || !textChatWindow.IsActive) &&
                (settings.NotifyOnPublicChatMessages || !isPublicChatRoom);
        }
    }

    [Export(typeof(INotificationSource<>))]
    [Export(typeof(IMessageNotificationSource))]
    [PartCreationPolicy(CreationPolicy.Shared)]
    public sealed class MessageNotificationSource : Subject<IAddMessageUpdate>, IMessageNotificationSource
    {
    }

    [Export(typeof(INotificationSource<>))]
    [Export(typeof(ILoginNotificationSource))]
    [PartCreationPolicy(CreationPolicy.Shared)]
    public sealed class LoginNotificationSource : Subject<ISessionCredentials>, ILoginNotificationSource
    {
    }

    [Export(typeof(INotifier))]
    [PartCreationPolicy(CreationPolicy.Shared)]
    public sealed class SoundNotifier : INotifier
    {
        [Import]
        private IShredServices ShredServices { get; set; }

        [Import]
        private IMessageNotificationSource MessageNotificationSource { get; set; }

        [Import]
        private ILoginNotificationSource LoginNotificationSource { get; set; }

        [Import]
        private IUiSettingsSection UiSettings { get; set; }

        [Import]
        private ISoundNotifierSettings NotifierSettings { get; set; }

        private CompositeDisposable _subscription;

        private MediaPlayer _player = new MediaPlayer();

        public void Start()
        {
            Contract.Assume(_subscription == null);
            _subscription = new CompositeDisposable();
            Action<Exception> onError =
                ex =>
                {
                    Trace.WriteLine(ex.ToString());
                };
            Action<Type> playSound =
                notificationType =>
                {
                    var soundUri = (from i in UiSettings.GetTheme().Sounds where i.NotificationType == notificationType select i.SoundUri).SingleOrDefault();
                    if (soundUri != null)
                    {
                        _player.Open(soundUri);
                        _player.Play();
                    }
                };

            IDisposable subscription1 = MessageNotificationSource.Where(i => NotificationHelper.IsMessageNotificationNeeded(i, NotifierSettings)).ObserveOn(ShredServices.Dispatcher).Subscribe(
                notification =>
                {
                    try
                    {
                        playSound(typeof(IMessageNotificationSource));
                    }
                    catch (Exception ex)
                    {
                        onError(ex);
                    }
                },
                onError);
            _subscription.Add(subscription1);

            IDisposable subscription2 = LoginNotificationSource.ObserveOn(ShredServices.Dispatcher).Subscribe(
                notification =>
                {
                    try
                    {
                        playSound(typeof(ILoginNotificationSource));
                    }
                    catch (Exception ex)
                    {
                        onError(ex);
                    }
                },
                onError);
            _subscription.Add(subscription2);
        }

        public void Dispose()
        {
            if (_subscription != null)
            {
                _subscription.Dispose();
                _subscription = null;
            }
            _player.Close();
        }
    }

    [Export(typeof(INotifier))]
    [PartCreationPolicy(CreationPolicy.Shared)]
    public sealed class FlashWindowNotifier : INotifier
    {
        [Import]
        private IShredServices ShredServices { get; set; }

        [Import]
        private IMessageNotificationSource MessageNotificationSource { get; set; }

        [Import]
        private IFlashWindowNotifierSettings NotifierSettings { get; set; }

        private IDisposable _subscription;

        public void Start()
        {
            Contract.Assume(_subscription == null);
            Action<Exception> onError =
                ex =>
                {
                    Trace.WriteLine(ex.ToString());
                };

            _subscription = MessageNotificationSource.Where(i => NotificationHelper.IsMessageNotificationNeeded(i, NotifierSettings)).ObserveOn(ShredServices.Dispatcher).Subscribe(
                notification =>
                {
                    try
                    {
                        ShredTheLog.Controls.Extensions.SetIsFlashing(NotificationHelper.GetTextChatWindow(notification.RoomName), true);
                    }
                    catch (Exception ex)
                    {
                        onError(ex);
                    }
                },
                onError);
        }

        public void Dispose()
        {
            if (_subscription != null)
            {
                _subscription.Dispose();
                _subscription = null;
            }
        }
    }

    [Export(typeof(INotifier))]
    [PartCreationPolicy(CreationPolicy.Shared)]
    public sealed class BalloonTipNotifier : INotifier
    {
        [Import]
        private IShredServices ShredServices { get; set; }

        [Import]
        private IMessageNotificationSource MessageNotificationSource { get; set; }

        [Import]
        private IBalloonTipNotifierSettings NotifierSettings { get; set; }

        private IDisposable _subscription;

        public void Start()
        {
            Contract.Assume(_subscription == null);
            Action<Exception> onError =
                ex =>
                {
                    Trace.WriteLine(ex.ToString());
                };

            _subscription = MessageNotificationSource.Where(i => NotificationHelper.IsMessageNotificationNeeded(i, NotifierSettings)).ObserveOn(ShredServices.Dispatcher).Subscribe(
                notification =>
                {
                    try
                    {
                        var user = ShredServices.TextChatUsers.SingleOrDefault(i => i.Uid == notification.Uid);

                        // TODO: Переделать понадежнее...
                        var userName = user != null ? user.ToString() : notification.Uid.ToString();

                        ShredServices.ShowInfoBalloonTip(userName, notification.Text);
                    }
                    catch (Exception ex)
                    {
                        onError(ex);
                    }
                },
                onError);
        }

        public void Dispose()
        {
            if (_subscription != null)
            {
                _subscription.Dispose();
                _subscription = null;
            }
        }
    }
}
